home *** CD-ROM | disk | FTP | other *** search
Text File | 1992-09-02 | 9.6 KB | 251 lines | [TEXT/CCL2] |
- ;;; scatter-plot-view.lisp
- ;;;
- ;;; Paul McCartney, Spring 1992
- ;;;
- ;;; Copyright © 1992 Paul McCartney. All Rights Reserved.
- ;;;
- ;;; Washington University Medical Informatics Training Program
- ;;;
- ;;; DESCRIPTION:
- ;;;
- ;;; This is a generalized view for a scatter plot. Given a list of data points,
- ;;; create a view to display the data in a two-dimensionally. This module,
- ;;; by itself, provides the control for this process but does not provide the
- ;;; specifics for particular scatter plots. These objects should be specialized
- ;;; with particular functions being overridden.
- ;;;
- ;;; USE:
- ;;;
- ;;; scatter-plot-view - view object for the scatter plot view.
- ;;; :x-start - horizontal start value
- ;;; :x-end - horizontal end value
- ;;; :y-start - vertical start value
- ;;; :y-end - vertical end value
- ;;; :inverted-p - whether the vertical start begins at the top (nil)
- ;;; or the bottom (t)
- ;;; :x-value-fn - return the x component of a data value
- ;;; :y-value-fn - return the y component of a data value
- ;;; :click-on-point-function - function to be performed when a point is clicked on
- ;;;
- ;;; scatter-plot-point - point object for data in the scatter plot view.
- ;;; add-scatter-plot-points - add a set of data to the scatter plot
- ;;; set-scatter-plot-x-scale - set the scatter plot x scale
- ;;; set-scatter-plot-y-scale - set the scatter plot y scale
- ;;; set-scatter-plot-origin - set the data values for the upper left corner of the
- ;;; view and scroll as needed.
- ;;; draw-scatter-plot-point - method to draw scatter-plot-point
- ;;;
- ;;; HISTORY:
- ;;;
- ;;; 7/17/92 Optimized scrolling. - PM
- ;;; 6/3/92 Created. - PM
- ;;;
-
- (in-package :ccl)
-
- (require :quickdraw)
- (require :GWorld-view-extensions)
-
- (eval-when (:compile-toplevel :load-toplevel :execute)
- (export '(scatter-plot-view scatter-plot-point add-scatter-plot-points
- set-scatter-plot-range scatter-plot-points draw-scatter-plot-point
- spp-data spp-topleft spp-bottomright)
- :ccl))
-
-
- ;;; This is the view object for the scatter plot view. It should not be
- ;;; used directly; instead, it should be specialized, overriding its key
- ;;; functions.
- ;;;
- (defclass scatter-plot-view (view)
- ((scatter-plot-points :initarg :scatter-plot-points :accessor scatter-plot-points)
- (inverted-p :initarg :inverted-p :accessor inverted-p)
- (x-scale :initarg :x-scale :accessor x-scale)
- (y-scale :initarg :y-scale :accessor y-scale)
- (x-start :initarg :x-start :accessor x-start)
- (y-start :initarg :y-start :accessor y-start)
- (x-end :initarg :x-end :accessor x-end)
- (y-end :initarg :y-end :accessor y-end)
- (x-value-fn :initarg :x-value-fn :accessor x-value-fn)
- (y-value-fn :initarg :y-value-fn :accessor y-value-fn)
- (click-on-point-function :initarg :click-on-point-function :accessor click-on-point-function)
- (click-on-hidden-points-p :initarg :click-on-hidden-points-p :accessor click-on-hidden-points-p)
- )
- (:default-initargs
- :view-position #@(0 0)
- :view-size #@(100 100)
- :scatter-plot-points nil
- :inverted-p t
- :x-scale 1
- :y-scale 1
- :x-start 0
- :x-end 10
- :y-start 0
- :y-end 10
- :x-value-fn #'first
- :y-value-fn #'second
- :click-on-point-function #'(lambda (view point) (declare (ignore view point)))
- :click-on-hidden-points-p nil
- )
- )
-
-
- ;;; This is the view object for the scatter plot point. It should not be
- ;;; used directly; instead, it should be specialized, overriding its key
- ;;; functions.
- ;;;
- (defclass scatter-plot-point ()
- ((spp-data :initarg :spp-data :accessor spp-data)
- (spp-size :initarg :spp-size :accessor spp-size)
- (spp-topleft :initarg :spp-topleft :accessor spp-topleft)
- (spp-bottomright :initarg :spp-bottomright :accessor spp-bottomright))
- )
-
-
- ;;;;
- ;;;; SCATTER PLOT VIEW
- ;;;;
-
- (defmethod set-view-size ((view scatter-plot-view) h &optional v)
- (call-next-method view h v)
- (set-scatter-plot-range view (x-start view) (x-end view) (y-start view) (y-end view))
- (set-point-positions view))
-
-
- ;;; This is a special view-draw-contents that takes care of determining if
- ;;; a scatter plot point is visible and should be displayed. If so, the
- ;;; generic function "draw-scatter-plot-point" is called with the point and
- ;;; the view.
- ;;;
- (defmethod view-draw-contents ((view scatter-plot-view))
- (let* ((region (intersect-region (rref (wptr view) grafport.visrgn)
- (rref (wptr view) grafport.cliprgn)))
- (top (href region :Region.rgnBBox.top))
- (left (href region :Region.rgnBBox.left))
- (right (href region :Region.rgnBBox.right))
- (bottom (href region :Region.rgnBBox.bottom)))
- (with-GWorld-no-colorization (view left top right bottom)
- (dolist (point (scatter-plot-points view))
- (when (rect-in-region-p region (spp-topleft point) (spp-bottomright point))
- (draw-scatter-plot-point
- point
- *GW-offscreen-view*
- (make-GW-point (spp-topleft point))
- (make-GW-point (spp-bottomright point))) )))
- (dispose-region region) ))
-
-
- (defmethod view-click-event-handler ((view scatter-plot-view) where)
- (call-next-method)
- (let ((done nil))
- (dolist (point (reverse (scatter-plot-points view)))
- (rlet ((r :rect
- :topleft (spp-topleft point)
- :bottomright (spp-bottomright point)))
- (when (and (not done) (point-in-rect-p r where))
- (when (not (click-on-hidden-points-p view))
- (setf done t))
- (funcall (click-on-point-function view) view (spp-data point))))) ))
-
-
- (defmethod add-scatter-plot-points ((view scatter-plot-view) data-points
- &optional (type 'scatter-plot-point)
- (clear nil)
- (size 5))
- (if clear
- (setf (scatter-plot-points view) ()))
-
- (dolist (data data-points)
- (let ((point (make-instance type :spp-data data :spp-size size)))
- (push point (scatter-plot-points view))))
-
- (set-point-positions view) )
-
-
- (defmethod set-point-positions ((view scatter-plot-view))
- (dolist (point (scatter-plot-points view))
- (let* ((x (point-value-in-range (scatter-plot-point-x-value view (spp-data point))))
- (y (point-value-in-range (scatter-plot-point-y-value view (spp-data point))))
- (half-size (point-value-in-range (round (spp-size point) 2)))
- (half-size-point (make-point half-size half-size)))
-
- (setf (spp-topleft point) (subtract-points (make-point x y) half-size-point))
- (setf (spp-bottomright point) (add-points (make-point x y) half-size-point)) )))
-
-
- (defmethod scatter-plot-point-x-value ((view scatter-plot-view) data &optional value)
- (round (* (x-scale view)
- (- (or value (funcall (x-value-fn view) data)) (x-start view)))))
-
-
- (defmethod scatter-plot-point-y-value ((view scatter-plot-view) data &optional value)
- (let ((y-pos (round (* (y-scale view)
- (- (or value (funcall (y-value-fn view) data)) (y-start view))))))
- (if (inverted-p view)
- (- (point-v (view-size view)) y-pos)
- y-pos)))
-
-
- (defmethod set-scatter-plot-range ((view scatter-plot-view) x-start x-end y-start y-end)
- (let ((old-x-scale (x-scale view))
- (old-y-scale (y-scale view))
- (old-x-start (x-start view))
- (old-y-start (y-start view))
- (x-scale (/ (point-h (view-size view)) (- x-end x-start)))
- (y-scale (/ (point-v (view-size view)) (- y-end y-start))))
- (setf (x-start view) x-start)
- (setf (x-end view) x-end)
- (setf (y-start view) y-start)
- (setf (y-end view) y-end)
- (setf (x-scale view) x-scale)
- (setf (y-scale view) y-scale)
-
- (let* ((scroll-change (or (/= old-x-start x-start)
- (/= old-y-start y-start)))
- (scale-change (or (/= x-scale old-x-scale) (/= y-scale old-y-scale))))
- (when (or scale-change scroll-change)
- (set-point-positions view))
-
- (cond ((and scroll-change (not scale-change))
- (scatter-plot-view-scroll view old-x-start old-y-start x-start y-start))
- ((or scale-change scroll-change)
- (invalidate-view view))) )))
-
-
- (defmethod scatter-plot-view-scroll ((view scatter-plot-view) old-x-start old-y-start x-start y-start)
- (let* ((dh (- (scatter-plot-point-x-value view nil old-x-start)
- (scatter-plot-point-x-value view nil x-start)))
- (dv (if (inverted-p view)
- (- (scatter-plot-point-y-value view nil old-y-start)
- (scatter-plot-point-y-value view nil y-start))
- (- (scatter-plot-point-y-value view nil y-start)
- (scatter-plot-point-y-value view nil old-y-start)))))
- (with-focused-view view
- (rlet ((rect :rect :topleft #@(0 0) :bottomright (view-size view)))
- (let* ((reg (#_newrgn)))
- (#_ScrollRect :ptr rect
- :long (make-point dh dv)
- :ptr reg)
- (#_invalrgn reg)
- (#_disposergn reg)))) ))
-
-
- ;;;;
- ;;;; SCATTER PLOT POINT GENERIC FUNCTIONS
- ;;;;
-
- ;;; GENERIC FUNCTION: Specialize me
- ;;;
- ;;; This is a generic function which should be overriden.
- ;;; By default, draw a filled in circle. Inherited functions should NOT
- ;;; call-next-method, as this would draw the circle on top of whatever else
- ;;; is drawn first. Note that the view is focused when this is called, so
- ;;; focusing is not necessary.
- ;;;
- (defmethod draw-scatter-plot-point ((point scatter-plot-point) view topleft bottomright)
- (rlet ((r :rect :topleft topleft :bottomright bottomright))
- (with-focused-view view
- (#_PaintOval r)) ))
-
-
- (provide :scatter-plot-view)